#pragma once

#include <cuda_runtime.h>
#include "..\FTLib\IPerformanceData.h"
#include "utils.h"
#include <vector>

class CSynchronizingCudaStopwatch
{
private:
	cudaEvent_t startEvent, stopEvent;
	cudaStream_t stream;
	float timeResult;
public:
	CSynchronizingCudaStopwatch(cudaStream_t stream);
	~CSynchronizingCudaStopwatch();

	void InsertStartEvent();

	void InsertStopEvent();

	double SyncAndGetTiming();
};

class CPerfClass
{
private:
	//CSynchronizingCudaStopwatch stopWatch;
	FTLib::IPerformanceDataStore* perfData;
	CStopWatch wallclockStopWatch;
	std::vector<cudaEvent_t> events;
	cudaStream_t stream;
	int currentCudaEvent;
public:
	CPerfClass(cudaStream_t stream, FTLib::IPerformanceDataStore* perfData);

	/// <summary>	Reserve the specified number of events. This method may only be called
	/// 			once and before any event is inserted, otherwise an exception is thrown.
	/// 			</summary>
	/// <param name="count">	Number of events to allocate and prepare.</param>
	void ReserveEvents(int count);
		
	/// <summary>
	/// Inserts an event into the stream and return the number of this event. A new event will be
	/// created if the pre-allocated number of events is outrun.
	/// </summary>
	/// <returns>The number of the event that was inserted into the queue.</returns>
	int InsertCudaEvent();

	void SychnronizeToEvent(int eventNo);

	void ReportDurationBetweenEvent(int eventFirst, int eventSecond, const wchar_t* str);

	void StartWallclock();
	void StopWallclock();
	void ReportDurationWallclock(const wchar_t* str);
};

class CNullPerfClass
{
public:
	CNullPerfClass(){};

	void ReserveEvents(int count){};
	int InsertCudaEvent(){ return -1; };
	void SychnronizeToEvent(int eventNo){};
	void ReportDurationBetweenEvent(int eventFirst, int eventSecond, const wchar_t* str){};

	void StartWallclock(){};
	void StopWallclock(){};
	void ReportDurationWallclock(const wchar_t* str){};
};